'use strict';

exports.name = 'convertStyleToAttrs';

exports.type = 'perItem';

exports.active = false;

exports.description = 'converts style to attributes';

exports.params = {
  keepImportant: false,
};

var stylingProps = require('./_collections').attrsGroups.presentation,
  rEscape = '\\\\(?:[0-9a-f]{1,6}\\s?|\\r\\n|.)', // Like \" or \2051. Code points consume one space.
  rAttr = '\\s*(' + g('[^:;\\\\]', rEscape) + '*?)\\s*', // attribute name like ‘fill’
  rSingleQuotes = "'(?:[^'\\n\\r\\\\]|" + rEscape + ")*?(?:'|$)", // string in single quotes: 'smth'
  rQuotes = '"(?:[^"\\n\\r\\\\]|' + rEscape + ')*?(?:"|$)', // string in double quotes: "smth"
  rQuotedString = new RegExp('^' + g(rSingleQuotes, rQuotes) + '$'),
  // Parentheses, E.g.: url(...).
  // ':' and ';' inside of it should be threated as is. (Just like in strings.)
  rParenthesis =
    '\\(' + g('[^\'"()\\\\]+', rEscape, rSingleQuotes, rQuotes) + '*?' + '\\)',
  // The value. It can have strings and parentheses (see above). Fallbacks to anything in case of unexpected input.
  rValue =
    '\\s*(' +
    g(
      '[^!\'"();\\\\]+?',
      rEscape,
      rSingleQuotes,
      rQuotes,
      rParenthesis,
      '[^;]*?'
    ) +
    '*?' +
    ')',
  // End of declaration. Spaces outside of capturing groups help to do natural trimming.
  rDeclEnd = '\\s*(?:;\\s*|$)',
  // Important rule
  rImportant = '(\\s*!important(?![-(\\w]))?',
  // Final RegExp to parse CSS declarations.
  regDeclarationBlock = new RegExp(
    rAttr + ':' + rValue + rImportant + rDeclEnd,
    'ig'
  ),
  // Comments expression. Honors escape sequences and strings.
  regStripComments = new RegExp(
    g(rEscape, rSingleQuotes, rQuotes, '/\\*[^]*?\\*/'),
    'ig'
  );

/**
 * Convert style in attributes. Cleanups comments and illegal declarations (without colon) as a side effect.
 *
 * @example
 * <g style="fill:#000; color: #fff;">
 *             ⬇
 * <g fill="#000" color="#fff">
 *
 * @example
 * <g style="fill:#000; color: #fff; -webkit-blah: blah">
 *             ⬇
 * <g fill="#000" color="#fff" style="-webkit-blah: blah">
 *
 * @param {Object} item current iteration item
 * @return {Boolean} if false, item will be filtered out
 *
 * @author Kir Belevich
 */
exports.fn = function (item, params) {
  if (item.type === 'element' && item.attributes.style != null) {
    // ['opacity: 1', 'color: #000']
    let styles = [];
    const newAttributes = {};

    // Strip CSS comments preserving escape sequences and strings.
    const styleValue = item.attributes.style.replace(
      regStripComments,
      (match) => {
        return match[0] == '/'
          ? ''
          : match[0] == '\\' && /[-g-z]/i.test(match[1])
          ? match[1]
          : match;
      }
    );

    regDeclarationBlock.lastIndex = 0;
    // eslint-disable-next-line no-cond-assign
    for (var rule; (rule = regDeclarationBlock.exec(styleValue)); ) {
      if (!params.keepImportant || !rule[3]) {
        styles.push([rule[1], rule[2]]);
      }
    }

    if (styles.length) {
      styles = styles.filter(function (style) {
        if (style[0]) {
          var prop = style[0].toLowerCase(),
            val = style[1];

          if (rQuotedString.test(val)) {
            val = val.slice(1, -1);
          }

          if (stylingProps.includes(prop)) {
            newAttributes[prop] = val;

            return false;
          }
        }

        return true;
      });

      Object.assign(item.attributes, newAttributes);

      if (styles.length) {
        item.attributes.style = styles
          .map((declaration) => declaration.join(':'))
          .join(';');
      } else {
        delete item.attributes.style;
      }
    }
  }
};

function g() {
  return '(?:' + Array.prototype.join.call(arguments, '|') + ')';
}
